<?php

	/**
	 * @see Bike_Initial
	 */
	require_once 'Bike/Initial.php';
	
	/**
	 * @see Bike_Form_Element_Interface_RenderHandlers
	 */
	require_once 'Bike/Form/Element/Interface/RenderHandlers.php';
	
	/**
	 * @see Bike_String
	 */
	require_once 'Bike/String.php';
	
	/**
	 * @see Zend_Reflection_Class
	 */
	require_once 'Zend/Reflection/Class.php';
	
	/**
	 * @see Bike_Form_Renderer_Standard
	 */
	require_once 'Bike/Form/Renderer/Standard.php';
	
	/**
	 * @see Bike_Form_Renderer
	 */
	require_once 'Bike/Form/Renderer.php';
	
	
	class Bike_Form_Element extends Bike_Initial implements Bike_Form_Element_Interface_RenderHandlers
	{		
		const ALIGN_AFTER = 'after';
		const ALIGN_BEFORE = 'before';
		
		protected $_element;
		protected $_showForRepeating = True;
		protected $_showFilteredValue = True;
		protected $_parseInt = True;
		protected $_settings = array();
		protected $_defaultValue;
		protected $_mark;
		protected $_markAlign = self::ALIGN_AFTER; // before | after
		
		public function init()
		{
			try
			{
				$this->getElement()->initElement($this);
			}
			catch(Zend_Form_Element_Exception $e)
			{
			}
			
			$this->addDecoratorAfter('ViewHelper', 'Mark', array('tag' => 'span', 'bikeElement' => $this));
		}
		
		protected function setOptionHandlerSettings($Settings)
		{
			
		}

		public function getSettings($Name=null)
		{
			if(is_null($Name))
			{
				return (array) $this->_settings;
			}
			if($this->issetSetting($Name))
			{
				return $this->_settings[$this->lowerCaseFirst($Name)];
			}
			return Null;
		}

		public function getSetting($Name)
		{
			return $this->getSettings($this->lowerCaseFirst($Name));
		}

		public function issetSetting($Name)
		{
			return isset($this->_settings[$this->lowerCaseFirst($Name)]);
		}

		public function unsetSetting($Name)
		{
			unset($this->_settings[$this->lowerCaseFirst($Name)]);
		}

		public function setSetting($Name, $Value)
		{
			$this->_settings[$this->lowerCaseFirst($Name)] = $Value;
			return $this;
		}

		public function setSettings(array $Settings=null)
		{
			if(!is_array($this->_settings))
			{
				$this->_settings = array();
			}
			foreach($Settings as $name => $value)
			{
				$this->setSetting($name, $value);
			}
			return $this;
		}

		protected function lowerCaseFirst($Name)
		{
			return Bike_String::factory($Name)->lowerCaseFirst();
		}

		public function setElement(Zend_Form_Element $Element)
		{			$this->_element = $Element;
			return $this;
		}

		public function getElement()
		{			return $this->_element;
		}
		
		/*public function setFilters(array $filters)
		{
			foreach($filters as $name => $options)
			{
				$this->addFilter($name, $options);
			}
			return $this;
		}*/
		
		public function setParseInt($flag=True)
		{
			$this->_parseInt = $flag;
			return $this;
		}
		
		public function getParseInt()
		{
			return $this->_parseInt;
		}

		public function setShowForRepeating($Flag)
		{			$this->_showForRepeating = (bool) $Flag;
			return $this;
		}

		public function showForRepeating()
		{
			return $this->_showForRepeating;
		}
		
		public function setShowFilteredValue($Flag)
		{
			$this->_showFilteredValue = (bool) $Flag;
			return $this;
		}
		
		public function showFilteredValue()
		{
			return $this->_showFilteredValue;
		}
		
		public function setMark($mark)
		{
			$this->_mark = $mark;
			return $this;
		}
		
		public function getMark()
		{
			return $this->_mark;
		}
		
		public function setMarkAlign($markAlign)
		{
			$this->_markAlign = $markAlign;
			return $this;
		}
		
		public function getMarkAlign()
		{
			return $this->_markAlign;
		}

		public function __set($Name, $Value)
	    {
	        try
	        {
	        	return parent::__set($Name, $Value);
	        }
	        catch(Bike_Exception $e)
	        {
		        return $this->getElement()->__set($Name, $Value);
			}
	    }

	    public function __get($Name)
	    {
	        try
	        {
	        	return parent::__get($Name);
	      	}
	      	catch(Bike_Exception $e)
	      	{	      		return $this->getElement()->__get($Name);
	      	}
	    }

		public function __call($MethodName, array $Args=null)
		{
			try
			{				return parent::__call($MethodName, $Args);
			}
			catch(Bike_Exception $e)
			{
				try
				{
					$result = call_user_func_array(array($this->getElement(), $MethodName), $Args);
					if(in_array(substr($MethodName,0,3), array('set', 'add')))
					{						return $this;
					}
					return $result;
				}
				catch(Zend_Form_Element_Exception $e)
				{					if(substr($MethodName, 0, 3) == 'get')
		    		{
		    			$Name = substr($MethodName, 3);
		    			$Value = $this->getSetting($Name);
		    			if(method_exists($this, 'processGet'.$Name))
				        {
				        	$Value = call_user_func(array($this, 'processGet'.$Name), $Value);
				        }
		    			return $Value;
		    		}
		    		elseif(substr($MethodName, 0, 5) == 'unset')
		    		{
		    			$Name = substr($MethodName, 5);
		    			return $this->unsetSetting($Name);
		    		}
		    		elseif(substr($MethodName, 0, 5) == 'isset')
		    		{
		    			$Name = substr($MethodName, 5);
		    			return $this->issetSetting($Name);
		    		}
		    		elseif(substr($MethodName, 0, 3) == 'set')
		    		{
		    			$Name = substr($MethodName, 3);
		    			if(sizeof($Args) <= 1)
		    			{
		    				$Value = reset($Args);
		    			}
		    			else
		    			{
		    				$Value = $Args;
		    			}
		    			if(method_exists($this, 'processSet'.$Name))
				        {
				        	$value = call_user_func(array($this, 'processSet'.$Name), $Value);
				        }
				        else
				        {
				        	$value = $Value;
				        }
		    			$this->setSetting($Name, $value);
		    			return $this;
		    		}
				}
			}
		}
		
		protected function applyBeforeRenderHandlers($object, $renderType, $view)
		{
			if($object instanceof Bike_Form_Element_Interface_RenderHandlers)
			{
				$reflection = new Zend_Reflection_Class($object);
				$prefix = $reflection->getConstant('HANDLER_PREFIX_BEFORE_RENDER');
				$len = strlen($prefix);
				$methods = $reflection->getMethods();
				foreach($methods as $rMethod)
				{
					$methodName = $rMethod->getName();
					if(substr($methodName, 0, $len) == $prefix && strlen($methodName) > $len)
					{
						$object->$methodName($renderType, $view);
					}
				}
			}			
		}
		
		protected function applyAfterRenderHandlers($object, $renderType, $rendered, $view)
		{
			if($object instanceof Bike_Form_Element_Interface_RenderHandlers)
			{
				$reflection = new Zend_Reflection_Class($object);
				$prefix = $reflection->getConstant('HANDLER_PREFIX_AFTER_RENDER');
				$len = strlen($prefix);
				$methods = $reflection->getMethods();
				foreach($methods as $rMethod)
				{
					$methodName = $rMethod->getName();
					if(substr($methodName, 0, $len) == $prefix && strlen($methodName) > $len)
					{
						$rendered = $object->$methodName($renderType, $rendered, $view);
					}
				}
			}
			return $rendered;
		}

		public function render(Zend_View_Interface $view = null, $renderType=Bike_Form_Renderer_Standard::TYPE)
		{
			$this->applyBeforeRenderHandlers($this, $renderType, $view);
			$this->applyBeforeRenderHandlers($this->getElement(), $renderType, $view);
			$rendered = Bike_Form_Renderer::factory($this, $renderType)->render($view);
			if($rendered)
			{
				$renderFlag = True;
			}
			$rendered = $this->applyAfterRenderHandlers($this, $renderType, $rendered, $view);
			$rendered = $this->applyAfterRenderHandlers($this->getElement(), $renderType, $rendered, $view);
			if($renderFlag && ! $rendered)
			{
				require_once 'Bike/Form/Exception.php';
				throw new Bike_Form_Exception('After render handler must return rendered value');
			}
			return $rendered;
		}

		public function standardRender()
		{
			$args = func_get_args();
			if(!$this->showForRepeating())
			{
				$value = $this->getElement()->getUnfilteredValue();
				$this->getElement()->setValue('');
			}
			$rendered = call_user_func_array(array($this->getElement(), 'render'), $args);
			if(!$this->showForRepeating())
			{
				$this->getElement()->setValue($value);
			}
			return $rendered;
		}
		
		public function getDefaultValue()
		{
			return $this->_defaultValue;
		}
		
		public function setDefaultValue($defaultValue)
		{
			$this->_defaultValue = $defaultValue;
			return $this;
		}
		
		public function beforeRenderHandlerDefaultValue()
		{
			if(is_null($this->getUnfilteredValue()))
			{
				$this->setValue($this->getDefaultValue());
			}
		}		
		
		/**
		 * Get the decorator name. Example: Zend_Form_Decorator_Label => Label
		 *
		 * @param  string|Zend_Form_Decorator_Interface $decorator
		 * @return string
		 */
		public function getDecoratorName($decorator)
		{
			if($decorator instanceof Zend_Form_Decorator_Interface)
			{
				$className = get_class($decorator);
			}
			if(is_string($decorator) && strstr($decorator, '_Decorator_'))
			{
				$className = $decorator;
			}
			else 
			{
				return false;
			}
			preg_match('/_Decorator_(.*)$/s', $className, $matches);
			return $matches[1];
		}
		
		public function addDecoratorAfter($afterName, $decorator, $options)
		{
			$decorators = $this->getElement()->getDecorators();
			$this->getElement()->clearDecorators();
			
			foreach($decorators as $name => $decoratorObj)
			{
				$this->getElement()->addDecorator($decoratorObj);
				if($this->getDecoratorName($name) == $afterName)
				{
					$this->getElement()->addDecorator($decorator, $options);
				}
			}
		}
		
		public function addDecoratorBefore($beforeName, $decorator, $options)
		{
			$decorators = $this->getElement()->getDecorators();
			$this->getElement()->clearDecorators();
			foreach($decorators as $name => $decoratorObj)
			{
				if($this->getDecoratorName($name) == $beforeName)
				{
					$this->getElement()->addDecorator($decorator, $options);
				}
				$this->getElement()->addDecorator($decoratorObj);
			}
		}
	}
